#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>

#define N 128

// 01-socket 192.168.1.2  8000
// argv[0]   arv[1]       argv[2]
int main(int argc, char const *argv[])
{

    int sockfd, ret, newsockfd;
    struct sockaddr_in myaddr, client_addr;
    socklen_t addrlen;
    char buf[N] = {0};

    if (argc != 3)
    {
        fprintf(stderr, "错误:运行程序时请带入参数(./08-select ip port)\n");
        exit(-1);
    }

    // 创建一个socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
    printf("sockfd=%d\n", sockfd);

    memset(&myaddr, 0, sizeof(myaddr));           // 清空结构体
    memset(&client_addr, 0, sizeof(client_addr)); // 清空结构体
    myaddr.sin_family = AF_INET;                  // 什么类型的通信
    myaddr.sin_port = htons(atoi(argv[2]));       // 设置socket 的固定端口
    myaddr.sin_addr.s_addr = inet_addr(argv[1]);  // 把字符串ip转换成网络二进制的ip地址

    ret = bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr));
    if (ret < 0)
    {
        perror("bind");
        exit(-1);
    }

    ret = listen(sockfd, 10);
    if (ret < 0)
    {
        perror("listen");
        exit(-1);
    }

    // system("netstat -ant");
    addrlen = sizeof(client_addr); //这个赋值如果缺少, 可能会引发程序错误

    fd_set rdfs;

    //struct timeval tv = {5,0};
    struct timeval tv;

    while (1)
    {
        FD_ZERO(&rdfs);        // 清空监控表
        FD_SET(0, &rdfs);      // 把标准输入(0) 加入表中
        FD_SET(sockfd, &rdfs); // 把监听socket 也加入和监控表内

        // 这样我们可以同时监控 标准输入和sockfd 这2个文件
        // 执行这个函数后, 会轮询监控表中的文件状态, 如果有可读的, 就立即返回
        // 如果没有可读的就阻塞 , 直到有文件可读
        // select 函数调用后, 会对rdfs 进行写操作, rdfs的值发生变化
        //每一次都要对时间进行赋值，如果不赋值，下次就不会有5秒倒计时
        tv.tv_sec = 5;  //5秒
        tv.tv_usec = 0; //0微秒
        ret = select(sockfd + 1, &rdfs, NULL, NULL, &tv);
        if (ret == 0)//超时时间到，没有文件准备就绪
        {
            printf("超时时间到\n");
        }
        else if (ret < 0)
        {
            perror("select");
            exit(-1);
        }

        // 检查一下哪一个文件 可读
        for (int i = 0; i <= sockfd + 1; i++)
        {
            // 检查i 在监控表中是否可读 , 如果可读 , 返回为真
            // 如果不可读, 返回假
            if (FD_ISSET(i, &rdfs))
            {
                // 如果i 是标准输入, 表示有人输入了数据, 这个时候是可读的
                if (i == 0)
                {
                    fgets(buf, N, stdin);
                    buf[strlen(buf) - 1] = 0; // 消除'\n'
                    printf("readfrom stdin:%s\n", buf);
                }

                // 如果i 是sockfd, 表示有客户端发起了tcp的连接(三次握手)
                else if (i == sockfd)
                {
                    newsockfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen); // 接收客户端tcp连接请求
                    if (newsockfd < 0)
                    {
                        perror("accept");
                        exit(-1);
                    }

                    printf("newsockfd=%d\n", newsockfd);
                    printf("%s & %d is connected\n", inet_ntoa(client_addr.sin_addr),
                           ntohs(client_addr.sin_port));

                    close(newsockfd);
                }
            }
        }
    }

    close(sockfd);

    return 0;
}
